#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "Namespace.h"
#include "RedefinableElement.h"
#include "TemplateableElement.h"
#include "Type.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class ClassifierTemplateParameter;
class CollaborationUse;
class Feature;
class Generalization;
class GeneralizationSet;
class Interface;
class NamedElement;
class Property;
class RedefinableTemplateSignature;
class StructuralFeature;
class Substitution;
class Type;
class UseCase;

/// <summary>
/// Classifier
/// A Classifier represents a classification of instances according to their
/// Features.
/// </summary>
class UMLMODEL_EXPORT Classifier : public Type, public RedefinableElement, public TemplateableElement, public Namespace {

public: // Constructors and destructors
    explicit Classifier(const QString& umlType="uml::Classifier");

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// allFeatures
    /// The query allFeatures() gives all of the Features in the namespace of the
    /// Classifier. In general, through mechanisms such as inheritance, this will be a
    /// larger set than feature.
    /// </summary>
    virtual shared_ptr<vector<Feature>> allFeatures() const;

    /// <summary>
    /// allParents
    /// The query allParents() gives all of the direct and indirect ancestors of a
    /// generalized Classifier.
    /// </summary>
    virtual shared_ptr<vector<Classifier>> allParents() const;

    /// <summary>
    /// conformsTo
    /// The query conformsTo() gives true for a Classifier that defines a type that
    /// conforms to another. This is used, for example, in the specification of
    /// signature conformance for operations.
    /// </summary>
    bool conformsTo(shared_ptr<Type> other) const override;

    /// <summary>
    /// hasVisibilityOf
    /// The query hasVisibilityOf() determines whether a NamedElement is visible in the
    /// classifier. Non-private members are visible. It is only called when the argument
    /// is something owned by a parent.
    /// </summary>
    virtual bool hasVisibilityOf(shared_ptr<NamedElement> n) const;

    /// <summary>
    /// inherits
    /// The query inherit() defines how to inherit a set of elements passed as its
    /// argument.  It excludes redefined elements from the result.
    /// </summary>
    virtual shared_ptr<vector<NamedElement>> inherits(vector<shared_ptr<NamedElement>> inhs) const;

    /// <summary>
    /// inheritableMembers
    /// The query inheritableMembers() gives all of the members of a Classifier that may
    /// be inherited in one of its descendants, subject to whatever visibility
    /// restrictions apply.
    /// </summary>
    virtual shared_ptr<vector<NamedElement>> inheritableMembers(shared_ptr<Classifier> c) const;

    /// <summary>
    /// isTemplate
    /// The query isTemplate() returns whether this Classifier is actually a template.
    /// </summary>
    bool isTemplate() const override;

    /// <summary>
    /// maySpecializeType
    /// The query maySpecializeType() determines whether this classifier may have a
    /// generalization relationship to classifiers of the specified type. By default a
    /// classifier may specialize classifiers of the same or a more general type. It is
    /// intended to be redefined by classifiers that have different specialization
    /// constraints.
    /// </summary>
    virtual bool maySpecializeType(shared_ptr<Classifier> c) const;

    /// <summary>
    /// parents
    /// The query parents() gives all of the immediate ancestors of a generalized
    /// Classifier.
    /// </summary>
    virtual shared_ptr<vector<Classifier>> parents() const;

    /// <summary>
    /// directlyRealizedInterfaces
    /// The Interfaces directly realized by this Classifier
    /// </summary>
    virtual shared_ptr<vector<Interface>> directlyRealizedInterfaces() const;

    /// <summary>
    /// directlyUsedInterfaces
    /// The Interfaces directly used by this Classifier
    /// </summary>
    virtual shared_ptr<vector<Interface>> directlyUsedInterfaces() const;

    /// <summary>
    /// allRealizedInterfaces
    /// The Interfaces realized by this Classifier and all of its generalizations
    /// </summary>
    virtual shared_ptr<vector<Interface>> allRealizedInterfaces() const;

    /// <summary>
    /// allUsedInterfaces
    /// The Interfaces used by this Classifier and all of its generalizations
    /// </summary>
    virtual shared_ptr<vector<Interface>> allUsedInterfaces() const;

    /// <summary>
    /// isSubstitutableFor
    /// </summary>
    virtual bool isSubstitutableFor(shared_ptr<Classifier> contract) const;

    /// <summary>
    /// allAttributes
    /// The query allAttributes gives an ordered set of all owned and inherited
    /// attributes of the Classifier. All owned attributes appear before any inherited
    /// attributes, and the attributes inherited from any more specific parent
    /// Classifier appear before those of any more general parent Classifier. However,
    /// if the Classifier has multiple immediate parents, then the relative ordering of
    /// the sets of attributes from those parents is not defined.
    /// </summary>
    virtual shared_ptr<vector<Property>> allAttributes() const;

    /// <summary>
    /// allSlottableFeatures
    /// All StructuralFeatures related to the Classifier that may have Slots, including
    /// direct attributes, inherited attributes, private attributes in generalizations,
    /// and memberEnds of Associations, but excluding redefined StructuralFeatures.
    /// </summary>
    virtual shared_ptr<vector<StructuralFeature>> allSlottableFeatures() const;

public: // Properties
    /// <summary>
    /// attributes getter
    /// All of the Properties that are direct (i.e., not inherited or imported)
    /// attributes of the Classifier.
    /// </summary>
    vector<shared_ptr<Property>>& attributes();

    /// <summary>
    /// collaborationUses getter
    /// The CollaborationUses owned by the Classifier.
    /// </summary>
    vector<shared_ptr<CollaborationUse>>& collaborationUses();

    /// <summary>
    /// features getter
    /// Specifies each Feature directly defined in the classifier. Note that there may
    /// be members of the Classifier that are of the type Feature but are not included,
    /// e.g., inherited features.
    /// </summary>
    vector<shared_ptr<Feature>>& features();

    /// <summary>
    /// generals getter
    /// The generalizing Classifiers for this Classifier.
    /// </summary>
    vector<shared_ptr<Classifier>>& generals();

    /// <summary>
    /// generalizations getter
    /// The Generalization relationships for this Classifier. These Generalizations
    /// navigate to more general Classifiers in the generalization hierarchy.
    /// </summary>
    vector<shared_ptr<Generalization>>& generalizations();

    /// <summary>
    /// inheritedMembers getter
    /// All elements inherited by this Classifier from its general Classifiers.
    /// </summary>
    vector<shared_ptr<NamedElement>>& inheritedMembers();

    /// <summary>
    /// isAbstract getter
    /// If true, the Classifier can only be instantiated by instantiating one of its
    /// specializations. An abstract Classifier is intended to be used by other
    /// Classifiers e.g., as the target of Associations or Generalizations.
    /// </summary>
    bool isAbstract() const;

    /// <summary>
    /// isAbstract setter
    /// If true, the Classifier can only be instantiated by instantiating one of its
    /// specializations. An abstract Classifier is intended to be used by other
    /// Classifiers e.g., as the target of Associations or Generalizations.
    /// </summary>
    void setIsAbstract(bool value);

    /// <summary>
    /// isFinalSpecialization getter
    /// If true, the Classifier cannot be specialized.
    /// </summary>
    bool isFinalSpecialization() const;

    /// <summary>
    /// isFinalSpecialization setter
    /// If true, the Classifier cannot be specialized.
    /// </summary>
    void setIsFinalSpecialization(bool value);

    /// <summary>
    /// ownedTemplateSignature getter
    /// The optional RedefinableTemplateSignature specifying the formal template
    /// parameters.
    /// </summary>
    shared_ptr<RedefinableTemplateSignature> ownedTemplateSignature() const;

    /// <summary>
    /// ownedTemplateSignature setter
    /// The optional RedefinableTemplateSignature specifying the formal template
    /// parameters.
    /// </summary>
    void setOwnedTemplateSignature(shared_ptr<RedefinableTemplateSignature> value);

    /// <summary>
    /// ownedUseCases getter
    /// The UseCases owned by this classifier.
    /// </summary>
    vector<shared_ptr<UseCase>>& ownedUseCases();

    /// <summary>
    /// powertypeExtents getter
    /// The GeneralizationSet of which this Classifier is a power type.
    /// </summary>
    vector<shared_ptr<GeneralizationSet>>& powertypeExtents();

    /// <summary>
    /// redefinedClassifiers getter
    /// The Classifiers redefined by this Classifier.
    /// </summary>
    vector<shared_ptr<Classifier>>& redefinedClassifiers();

    /// <summary>
    /// representation getter
    /// A CollaborationUse which indicates the Collaboration that represents this
    /// Classifier.
    /// </summary>
    shared_ptr<CollaborationUse> representation() const;

    /// <summary>
    /// representation setter
    /// A CollaborationUse which indicates the Collaboration that represents this
    /// Classifier.
    /// </summary>
    void setRepresentation(shared_ptr<CollaborationUse> value);

    /// <summary>
    /// substitutions getter
    /// The Substitutions owned by this Classifier.
    /// </summary>
    vector<shared_ptr<Substitution>>& substitutions();

    /// <summary>
    /// templateParameter getter
    /// TheClassifierTemplateParameter that exposes this element as a formal parameter.
    /// </summary>
    shared_ptr<ClassifierTemplateParameter> templateParameter() const;

    /// <summary>
    /// templateParameter setter
    /// TheClassifierTemplateParameter that exposes this element as a formal parameter.
    /// </summary>
    void setTemplateParameter(shared_ptr<ClassifierTemplateParameter> value);

    /// <summary>
    /// useCases getter
    /// The set of UseCases for which this Classifier is the subject.
    /// </summary>
    vector<shared_ptr<UseCase>>& useCases();

private: // Fields
    /// <summary>
    /// attributes
    /// All of the Properties that are direct (i.e., not inherited or imported)
    /// attributes of the Classifier.
    /// </summary>
    vector<shared_ptr<Property>> attributes_;

    /// <summary>
    /// collaborationUses
    /// The CollaborationUses owned by the Classifier.
    /// </summary>
    vector<shared_ptr<CollaborationUse>> collaborationUses_;

    /// <summary>
    /// features
    /// Specifies each Feature directly defined in the classifier. Note that there may
    /// be members of the Classifier that are of the type Feature but are not included,
    /// e.g., inherited features.
    /// </summary>
    vector<shared_ptr<Feature>> features_;

    /// <summary>
    /// generals
    /// The generalizing Classifiers for this Classifier.
    /// </summary>
    vector<shared_ptr<Classifier>> generals_;

    /// <summary>
    /// generalizations
    /// The Generalization relationships for this Classifier. These Generalizations
    /// navigate to more general Classifiers in the generalization hierarchy.
    /// </summary>
    vector<shared_ptr<Generalization>> generalizations_;

    /// <summary>
    /// inheritedMembers
    /// All elements inherited by this Classifier from its general Classifiers.
    /// </summary>
    vector<shared_ptr<NamedElement>> inheritedMembers_;

    /// <summary>
    /// isAbstract
    /// If true, the Classifier can only be instantiated by instantiating one of its
    /// specializations. An abstract Classifier is intended to be used by other
    /// Classifiers e.g., as the target of Associations or Generalizations.
    /// </summary>
    const static bool isAbstract_default_;
    bool isAbstract_= isAbstract_default_;

    /// <summary>
    /// isFinalSpecialization
    /// If true, the Classifier cannot be specialized.
    /// </summary>
    const static bool isFinalSpecialization_default_;
    bool isFinalSpecialization_= isFinalSpecialization_default_;

    /// <summary>
    /// ownedTemplateSignature
    /// The optional RedefinableTemplateSignature specifying the formal template
    /// parameters.
    /// </summary>
    shared_ptr<RedefinableTemplateSignature> ownedTemplateSignature_;

    /// <summary>
    /// ownedUseCases
    /// The UseCases owned by this classifier.
    /// </summary>
    vector<shared_ptr<UseCase>> ownedUseCases_;

    /// <summary>
    /// powertypeExtents
    /// The GeneralizationSet of which this Classifier is a power type.
    /// </summary>
    vector<shared_ptr<GeneralizationSet>> powertypeExtents_;

    /// <summary>
    /// redefinedClassifiers
    /// The Classifiers redefined by this Classifier.
    /// </summary>
    vector<shared_ptr<Classifier>> redefinedClassifiers_;

    /// <summary>
    /// representation
    /// A CollaborationUse which indicates the Collaboration that represents this
    /// Classifier.
    /// </summary>
    shared_ptr<CollaborationUse> representation_;

    /// <summary>
    /// substitutions
    /// The Substitutions owned by this Classifier.
    /// </summary>
    vector<shared_ptr<Substitution>> substitutions_;

    /// <summary>
    /// templateParameter
    /// TheClassifierTemplateParameter that exposes this element as a formal parameter.
    /// </summary>
    shared_ptr<ClassifierTemplateParameter> templateParameter_;

    /// <summary>
    /// useCases
    /// The set of UseCases for which this Classifier is the subject.
    /// </summary>
    vector<shared_ptr<UseCase>> useCases_;

};

} // namespace uml
